/* package org.gridsofts.util */

var org;
if (!org) org = {};
else if (typeof org != "object")
	throw new Error("org already exists and is not an object");
	
if (!org.gridsofts) org.gridsofts = {};
else if (typeof org.gridsofts != "object")
	throw new Error("org.gridsofts already exists and is not object");

if (!org.gridsofts.util) org.gridsofts.util = {};
else if (typeof org.gridsofts.util != "object")
	throw new Error("org.gridsofts.util already exists and is not object");


/** 判断是否是IE */
org.gridsofts.util.IsIE = true;

if (!window.ActiveXObject) {
	org.gridsofts.util.IsIE = false;
} else {

	org.gridsofts.util.IEVersion = navigator.appVersion.match(/MSIE\s+\d+/i).toString();
	
	if (org.gridsofts.util.IEVersion != undefined) {
		org.gridsofts.util.IEVersion = org.gridsofts.util.IEVersion.replace(/MSIE\s+/gi, "");
	}
}



/**
 * 类相关工具函数
 * 
 * 作者：赵磊
 */
org.gridsofts.util.Class = {
	
	/**
	 * 类定义函数
	 * 		用于定义一个新类；
	 * 		除可以定义类的构造函数、方法、静态变量（方法）之外，还可以实现类的继承、方法借用、接口实现等。
	 * 
	 * 返回值：新类的构造函数
	 * 
	 * 参考：O'Reilly (R)《JavaScript权威指南》书中用例
	 */
	define:	function (data) {
		var classname = data.name;
		var superclass = data.extend || Object;
		var constructor = data.construct || function () {};
		var methods = data.methods || {};
		var statics = data.statics || {};
		var borrows;
		var provides;
		
		if (!data.borrows) borrows = [];
		else if (data.borrows instanceof Array) borrows = data.borrows;
		else borrows = [data.borrows];
		
		if (!data.provides) provides = [];
		else if (data.provides instanceof Array) provides = data.provides;
		else provides = [data.provides];
		
		var proto = new superclass();
		
		for (var p in proto) 
			if (proto.hasOwnProperty(p)) delete proto[p];
		
		for (var i = 0; i < borrows.length; i++) {
			var c = borrows[i];
			for (var p in c.prototype) {
				if (typeof c.prototype[p] == "function") {
					proto[p] = c.prototype[p];
				}
			}
		}
		
		for (var p in methods) proto[p] = methods[p];
		
		proto.constructor = constructor;
		proto.superclass = superclass;
		
		if (classname) proto.classname = classname;
		
		for (var i = 0; i < provides.length; i++) {
			var c = provides[i];
			for (var p in c.prototype) {
				if (typeof c.prototype[p] != "function") continue;
				if (p == "constructor" || p == "superclass") continue;
				
				if (p in proto && typeof proto[p] == "function" 
					&& proto[p].length == c.prototype[p].length) {
					
					continue;
				}
				
				throw new Error("Class " + classname + " does not provides method " + c.classname + "." + p);
			}
		}
		
		for (var p in statics) constructor[p] = statics[p];
		
		constructor.prototype = proto;
		
		return constructor;
	}
};

/**
 * 将数值V进行四舍五入，保留指定的小数位数E
 */
org.gridsofts.util.toFixed = function (v, e) {
	
	var t = 1;

	for (; e > 0; t *= 10, e--)
		;

	for (; e < 0; t /= 10, e++)
		;

	return Math.round(v * t) / t;
};


/* package org.gridsofts.ext.Handler */

if (!org.gridsofts.ext) org.gridsofts.ext = {};
else if (typeof org.gridsofts.ext != "object")
	throw new Error("org.gridsofts.ext already exists and is not object");

if (!org.gridsofts.ext.Handler) org.gridsofts.ext.Handler = {};
else if (typeof org.gridsofts.ext.Handler != "object")
	throw new Error("org.gridsofts.ext.Handler already exists and is not object");


/**
 * 通用的添加事件侦听函数
 *
 * 参考：O'Reilly (R)《JavaScript权威指南》书中用例，并于作者网站下载 -- http://www.davidflanagan.com/javascript5
 * 
 * Handler.js -- Portable event handler registration functions
 *
 * This module defines event handler registration and deregistration functions
 * Handler.add() and Handler.remove(). Both functions take three arguments:
 * 
 *   element: the DOM element, document, or window on which the handler
 *      is to be added or removed.
 * 
 *   eventType: a string that specifies the type of event for which the
 *      handler is to be invoked.  Use DOM-standard type names, which do
 *      not include an "on" prefix. Examples: "click", "load", "mouseover".
 *
 *   handler: The function to be invoked when an event of the specified type
 *      occurs on the specified element.  This function will be invoked as
 *      a method of the element on which it is registered, and the "this"
 *      keyword will refer to that element.  The handler function will be
 *      passed an event object as its sole argument.  This event object will
 *      either be a DOM-standard Event object or a simulated one. If a 
 *      simulated event object is passed, it will have the following DOM-
 *      compliant properties: type, target, currentTarget, relatedTarget,
 *      eventPhase, clientX, clientY, screenX, screenY, altKey, ctrlKey,
 *      shiftKey, charCode, stopPropagation(), and preventDefault()
 * 
 * Handler.add() and Handler.remove() have no return value.
 * 
 * Handler.add() ignores duplicate registrations of the same handler for
 * the same event type and element.  Handler.remove() does nothing if called
 * to remove a handler that has not been registered.
 *
 * Implementation notes:
 * 
 * In browsers that support the DOM standard addEventListener() and
 * removeEventListener() event registration functions, Handler.add() and
 * Handler.remove() simply invoke these functions, passing false as the 
 * third argument (meaning that the event handlers are never registered as
 * capturing event handlers).
 * 
 * In versions of Internet Explorer that support attachEvent(), Handler.add()
 * and Handler.remove() use attachEvent() and detachEvent(). To
 * invoke the handler function with the correct this keyword, a closure is
 * used.  Since closures of this sort cause memory leaks in Internet Explorer,
 * Handler.add() automatically registers an onunload handler to deregister
 * all event handlers when the page is unloaded. To keep track of
 * registered handlers, Handler.add() creates a property named _allHandlers on
 * the window object and creates a property named _handlers on any element on
 * which a handler is registered.
 */

// In DOM-compliant browsers, our functions are trivial wrappers around
// addEventListener() and removeEventListener().
if (document.addEventListener) {
    org.gridsofts.ext.Handler.add = function(element, eventType, handler) {
        element.addEventListener(eventType, handler, false);
    };

    org.gridsofts.ext.Handler.remove = function(element, eventType, handler) {
        element.removeEventListener(eventType, handler, false);
    };
}
// In IE 5 and later, we use attachEvent() and detachEvent(), with a number of
// hacks to make them compatible with addEventListener and removeEventListener.
else if (document.attachEvent) {
    org.gridsofts.ext.Handler.add = function(element, eventType, handler) {
        // Don't allow duplicate handler registrations
        // _find() is a private utility function defined below.
        if (org.gridsofts.ext.Handler._find(element, eventType, handler) != -1) return;
        
        // To invoke the handler function as a method of the
        // element, we've got to define this nested function and register
        // it instead of the handler function itself.
        var wrappedHandler = function(e) {
            if (!e) e = window.event;

            // Create a synthetic event object with partial compatibility
            // with DOM events.
            var event = {
                _event: e,    // In case we really want the IE event object
                type: e.type,           // Event type
                target: e.srcElement,   // Where the event happened
                currentTarget: element, // Where we're handling it
                relatedTarget: e.fromElement?e.fromElement:e.toElement,
                eventPhase: (e.srcElement==element)?2:3,

                // Mouse coordinates
                clientX: e.clientX, clientY: e.clientY,
                screenX: e.screenX, screenY: e.screenY,
                
                // Key state
                altKey: e.altKey, ctrlKey: e.ctrlKey,
                shiftKey: e.shiftKey, charCode: e.keyCode,

                // Event management functions
                stopPropagation: function() {this._event.cancelBubble = true;},
                preventDefault: function() {this._event.returnValue = false;}
            };

            // Invoke the handler function as a method of the element, passing
            // the synthetic event object as its single argument.
            // Use Function.call() if defined; otherwise do a hack
            if (Function.prototype.call) 
                handler.call(element, event);
            else {
                // If we don't have Function.call, fake it like this
                element._currentHandler = handler;
                element._currentHandler(event);
                element._currentHandler = null;
            }
        };

        // Now register that nested function as our event handler.
        element.attachEvent("on" + eventType, wrappedHandler);
        
        // Now we must do some record keeping to associate the user-supplied
        // handler function and the nested function that invokes it.

        // We have to do this so that we can deregister the handler with the
        // remove() method and also deregister it automatically on page unload.

        // Store all info about this handler into an object
        var h = {
            element: element,
            eventType: eventType,
            handler: handler,
            wrappedHandler: wrappedHandler
        };

        // Figure out what document this handler is part of.
        // If the element has no "document" property, it is not
        // a window or a document element, so it must be the document
        // object itself.
        var d = element.document || element;
        // Now get the window associated with that document
        var w = d.parentWindow;

        // We have to associate this handler with the window,
        // so we can remove it when the window is unloaded
        var id = org.gridsofts.ext.Handler._uid();  // Generate a unique property name
        if (!w._allHandlers) w._allHandlers = {};  // Create object if needed
        w._allHandlers[id] = h; // Store the handler info in this object

        // And associate the id of the handler info with this element as well
        if (!element._handlers) element._handlers = [];
        element._handlers.push(id);

        // If there is not an onunload handler associated with the window,
        // register one now.
        if (!w._onunloadHandlerRegistered) {
            w._onunloadHandlerRegistered = true;
            w.attachEvent("onunload", org.gridsofts.ext.Handler._removeAllHandlers);
        }
    };

    org.gridsofts.ext.Handler.remove = function(element, eventType, handler) {
        // Find this handler in the element._handlers[] array.
        var i = org.gridsofts.ext.Handler._find(element, eventType, handler);
        if (i == -1) return;  // If the handler was not registered, do nothing

        // Get the window of this element
        var d = element.document || element;
        var w = d.parentWindow;

        // Look up the unique id of this handler
        var handlerId = element._handlers[i];
        // And use that to look up the handler info
        var h = w._allHandlers[handlerId];
        // Using that info, we can detach the handler from the element
        element.detachEvent("on" + eventType, h.wrappedHandler);
        // Remove one element from the element._handlers array
        element._handlers.splice(i, 1);
        // And delete the handler info from the per-window _allHandlers object
        delete w._allHandlers[handlerId];
    };

    // A utility function to find a handler in the element._handlers array
    // Returns an array index or -1 if no matching handler is found
    org.gridsofts.ext.Handler._find = function(element, eventType, handler) {
        var handlers = element._handlers;
        if (!handlers) return -1;  // if no handlers registered, nothing found

        // Get the window of this element
        var d = element.document || element;
        var w = d.parentWindow;

        // Loop through the handlers associated with this element, looking
        // for one with the right type and function.
        // We loop backward because the most recently registered handler
        // is most likely to be the first removed one.
        for(var i = handlers.length-1; i >= 0; i--) {
            var handlerId = handlers[i];        // get handler id
            var h = w._allHandlers[handlerId];  // get handler info
            // If handler info matches type and handler function, we found it.
            if (h.eventType == eventType && h.handler == handler) 
                return i;
        }
        return -1;  // No match found
    };

    org.gridsofts.ext.Handler._removeAllHandlers = function() {
        // This function is registered as the onunload handler with 
        // attachEvent.  This means that the this keyword refers to the
        // window in which the event occurred.
        var w = this;

        // Iterate through all registered handlers
        for(id in w._allHandlers) {
            // Get handler info for this handler id
            var h = w._allHandlers[id]; 
            // Use the info to detach the handler
            h.element.detachEvent("on" + h.eventType, h.wrappedHandler);
            // Delete the handler info from the window
            delete w._allHandlers[id];
        }
    };

    // Private utility to generate unique handler ids
    org.gridsofts.ext.Handler._counter = 0;
    org.gridsofts.ext.Handler._uid = function() { return "h" + org.gridsofts.ext.Handler._counter++; };
}


/* package org.gridsofts.ajax */
	
if (!org.gridsofts.ajax) org.gridsofts.ajax = {};
else if (typeof org.gridsofts.ajax != "object")
	throw new Error("org.gridsofts.ajax already exists and is not object");

/** 测试包可用性 org.gridsofts.util */
if (!org.gridsofts.util || !org.gridsofts.util.Class)
	throw new Error("org.gridsofts.util.js has not been loaded");


/* functions */
org.gridsofts.ajax.initHttpRequest = function () {
	
	var HttpRequest;
	
	if (window.XMLHttpRequest) { // Mozilla,...
		HttpRequest = new XMLHttpRequest();
	} else if (window.ActiveXObject) { // IE
		try {
			HttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
		} catch (e) {
			try {
				HttpRequest = new ActiveXObject("Microsoft.XMLHTTP");
			} catch (e) {}
		}
	}
	
	if (!HttpRequest) {
		throw new Error("Cannot create an XMLHTTP instance");
	}
	
	return HttpRequest;
};

org.gridsofts.ajax.parse = function (text) {
	
	if (window.DOMParser) { // Mozilla,...
		var parser = new DOMParser();
		
		return parser.parseFromString(text, "text/xml");
		
	} else if (window.ActiveXObject) { // IE
		
		var doc = new ActiveXObject("MSXML2.DOMDocument");
			doc.async = false; 
			doc.loadXML(text);
		
		return doc;
	}
};

org.gridsofts.ajax.getVariableString = function (data) {
	
	if (typeof data != "object") {
		return "";
	}

	var str = "";
	for (var p in data) {
		str += p + "=" + data[p] + "&";
	}
	
	return str;
};

/**
 * 将Xml解析成对象数组的函数，可以接受比较灵活的Xml结构，例：
 * 	<list>
 * 		<row colname_1="colvalue_1" colname_2="colvalue_2" .../>
 * 		... ...
 * 		<row colname_1="colvalue_1" colname_2="colvalue_2" ...>
 * 			<colname_3><![CDATA[colvalue_3]]></colname_3>
 * 			<colname_4>colvalue_4</colname_4>
 * 			... ...
 * 		</row>
 * 		... ...
 * 	</list>
 * 
 * 返回值形如：
 * 	[
 * 		{colname: colvalue},
 * 		... ...
 * 	]
 */
org.gridsofts.ajax.getResultArray = function (node) {
	
	var result = new Array();
	
	if (node && node.childNodes && node.childNodes.length) {
		
		for (var i = 0; i < node.childNodes.length; i++) {
			
			var child = node.childNodes[i];
			
			// Node.ELEMENT_NODE
			if (child.nodeType == 1) {
				result.push(org.gridsofts.ajax.getValueObject(child));
			}
		}
	}
	
	return result;
};
org.gridsofts.ajax.getValueObject = function (node) {
	
	var obj = null;
	
	// Node.ELEMENT_NODE
	if (node.nodeType == 1) {
		
		obj = new Object();
		
		for (var j = 0; j < node.attributes.length; j++) {
			var attr = node.attributes.item(j);
			
			obj[attr.name] = attr.value;
		}
	
		for (var j = 0; j < node.childNodes.length; j++) {
			var child = node.childNodes[j];
			
			obj[child.nodeName] = org.gridsofts.ajax.getValueObject(child);
		}
		
	} else {
		obj = node.nodeValue;
	}
	
	return obj;
};

/**
 * 以POST方式发送Ajax请求的快捷方法
 * 参数：url, onCompleteFun, onFaultFun, async, ...
 * 其中：url 和 onCompleteFun 是必选参数。最后一个参数是准备发送的数据
 */
org.gridsofts.ajax.post = function ($this, options) {
	
	if (!options || !options["url"]) {
		throw new Error("arguments invalid");
	}
	
	options["method"] = "POST";
	
	org.gridsofts.ajax.send($this, options);
};


/**
 * 以GET方式发送Ajax请求的快捷方法
 * 参数：url, onCompleteFun, onFaultFun, async, ...
 * 其中：url 和 onCompleteFun 是必选参数。最后一个参数是准备发送的数据
 */
org.gridsofts.ajax.get = function ($this, options) {
	
	if (!options || !options["url"]) {
		throw new Error("arguments invalid");
	}
	
	options["method"] = "GET";
	
	org.gridsofts.ajax.send($this, options);
};

/**
 * 发送Ajax请求的快捷方法
 * 参数：url, onCompleteFun, onFaultFun, async, ...
 * 其中：url 和 onCompleteFun 是必选参数。最后一个参数是准备发送的数据
 */
org.gridsofts.ajax.send = function ($this, options) {
	
	if (!options || !options["url"]) {
		throw new Error("arguments invalid");
	}
	
	var onCompleteFunc;
	if (options["onComplete"] && typeof options["onComplete"] == "function") {
		onCompleteFunc = options["onComplete"];
	}

	var onErrorFunc;
	if (options["onError"] && typeof options["onError"] == "function") {
		onErrorFunc = options["onError"];
	}

	var async = true;
	if (options["async"] && typeof options["async"] == "boolean") {
		async = options["async"];
	}

	$.ajax({
		method		: options["method"],
		url 		: options["url"],
		async  		: async,
		dataType	: options["dataType"],
		data		: options["data"]
	}).done(function(result) {
		onCompleteFunc($this, result);
	}).fail(function(result) {
		onErrorFunc($this, result);
	});
};

/**
 * AjaxGrid
 * 		以Ajax方式动态加载表格数据，支持分页
 * 
 * 作者：赵磊 
 */
org.gridsofts.ajax.Grid = org.gridsofts.util.Class.define({
	name:	"Grid",
	
	construct:
		function (options) {
			this.headers = new Array();
			this.renderer = new Object();
			
			this.pageNumer = 1;		// 当前页码（从1开始）
			this.pageCapacity = 10;	// 每页显示的最大记录行数

			this.maxPageCount = 0;		// 总页数
			this.maxRowCount = 0;		// 总记录数
			
			// 处理参数
			if (options && typeof options == "object") {
				
				this.id = options.id;
				
				this.dataSource = options.dataSource;	// 数据源（AJAX地址）
				this.dataType = options.dataType;		// 数据格式（支持XML、JSON两种格式）
				
				this.method = options.method; 			// Ajax请求发送方法
				
				this.showCheck = options.showCheck; 	// 是否显示复选框
				this.autoLoad = options.autoLoad; 		// 是否自动加载数据
				
				this.styleName = options.styleName;
				
				this.onBeforeLoad = options.onBeforeLoad;	// 加载数据之前调用
				this.onAfterLoad = options.onAfterLoad;		// 数据加载完毕后调用
				
				this.onRowMouseOver = options.onRowMouseOver;	// 
				this.onRowMouseOut = options.onRowMouseOut;		// 
				this.onRowClick = options.onRowClick;			// 
				this.onRowDblClick = options.onRowDblClick;		// 
				
				
			}
			
			// 生成随机ID
			if (!this.id) {
				this.id = "org.gridsofts.ajax.Grid." + new Date().getTime() * Math.random();
			}
		},
		
	methods:	{
		
		/**
		 * 获取选定的行ID
		 */
		getCheckedRowId:	function () {
			
			var checkedIdAry = new Array();
			
			var bindChkId = this.allCheckBox.getAttribute("bindChkId");
			var chkIdAry = bindChkId.split(",");
			
			for (var i = 0; chkIdAry && i < chkIdAry.length; i++) {
				
				var chk = document.getElementById(chkIdAry[i]);
				
				if (chk && chk.checked) {
					checkedIdAry.push(this.id + "_row_" + chk.value);
				}
			}
			
			return checkedIdAry;
		},
		
		/**
		 * 获取选定的行ID
		 */
		cleanRowChecked:	function () {
			
			var bindChkId = this.allCheckBox.getAttribute("bindChkId");
			var chkIdAry = bindChkId.split(",");
			
			for (var i = 0; chkIdAry && i < chkIdAry.length; i++) {
				
				var chk = document.getElementById(chkIdAry[i]);
				
				if (chk && chk.checked) {
					chk.checked = false;
				}
			}
		},
		
		/**
		 * 获取指定行的选中状态
		 */
		getRowChecked:	function (row) {
			var rowId = row.getAttribute("_rowId");
			
			if (rowId) {
				var chkId = rowId.replace(/_row_/gi, "_chk_");
				var chk = document.getElementById(chkId);
				
				if (chk) {
					return chk.checked;
				}
			}
			
			return false;
		},
		
		/**
		 * 将指定的行设为选中状态
		 */
		setRowChecked:	function (row, checked) {
			var rowId = row.getAttribute("_rowId");
			
			if (rowId) {
				var chkId = rowId.replace(/_row_/gi, "_chk_");
				var chk = document.getElementById(chkId);
				
				if (chk) {
					chk.checked = checked;
				}
			}
		},
		
		/**
		 * 设置ID
		 */
		setId:	function (id) {
			this.id = id;
		},
		
		/**
		 * 设置数据源
		 */
		setDataSource:	function (dataSource) {
			this.dataSource = dataSource;
		},
		
		/**
		 * 设置数据结果类型
		 */
		setDataType:	function (dataType) {
			this.dataType = dataType;
		},
		
		/**
		 * 设置是否显示复选框
		 */
		setShowCheck:	function (showCheck) {
			this.showCheck = showCheck;
		},
		
		/**
		 * 设置是否在画完表格后立即加载数据
		 */
		setAutoLoad:	function (autoLoad) {
			this.autoLoad = autoLoad;
		},
		
		/**
		 * 加载数据之前的方法
		 */
		setOnBeforeLoad:	function (onBeforeLoad) {
			this.onBeforeLoad = onBeforeLoad;
		},
		
		/**
		 * 数据加载之后的方法
		 */
		setOnAfterLoad:	function (onAfterLoad) {
			this.onAfterLoad = onAfterLoad;
		},
		
		/**
		 * 鼠标移动至行的方法
		 */
		setOnRowMouseOver:	function (onRowMouseOver) {
			this.onRowMouseOver = onRowMouseOver;
		},
		
		/**
		 * 鼠标移出行的方法
		 */
		setOnRowMouseOut:	function (onRowMouseOut) {
			this.onRowMouseOut = onRowMouseOut;
		},
		
		/**
		 * 鼠标单击行的方法
		 */
		setOnRowClick:	function (onRowClick) {
			this.onRowClick = onRowClick;
		},
		
		/**
		 * 鼠标双击行的方法
		 */
		setOnRowDblClick:	function (onRowDblClick) {
			this.onRowDblClick = onRowDblClick;
		},
		
		/**
		 * 设置分页动作URL
		 */
		setPagingAction:	function (pagingAction) {
			this.pagingAction = pagingAction;
		},
		
		/**
		 * 设置行渲染器
		 */
		setRenderer:	function (field, renderer) {
			
			if (field && renderer && typeof renderer == "function") {
				this.renderer[field] = renderer;
			}
		},
		
		/**
		 * 添加头
		 * 
		 * {
		 * 		label: "",
		 *  	field: "",
		 *  	styleName: ""
		 * }
		 */
		addHeaders:	function () {
			
			for (var i = 0; i < arguments.length; i++) {
				
				var arg = arguments[i];
				
				if (arg && typeof arg == "object") {
					this.headers.push(arg);
				}
			}
		},
		
		/**
		 * 在指定的位置画表格
		 */
		draw:	function (elementId) {
			var canvas = document.getElementById(elementId);
			
			// clean
			canvas.innerHTML = "";
			
			// start tag
			var table = document.createElement("table");
			canvas.appendChild(table);

			table.setAttribute("id", this.id);
			if (this.styleName) {
				table.setAttribute("class", this.styleName);
			} else {
				table.setAttribute("class", "org_gridsofts_ajax_Grid ");
			}
			
			var tbody = document.createElement("tbody");
			table.appendChild(tbody);
			
			var tr = document.createElement("tr");
			tbody.appendChild(tr);
			
			tr.setAttribute("class", "head");

			// init header
			if (this.showCheck) {
				
				var colCheck = document.createElement("th");
				tr.appendChild(colCheck);
				
				// 全选框
				if (org.gridsofts.util.IsIE) {
					
					switch (org.gridsofts.util.IEVersion) {
					
						case "6":
						case "7":
						case "8":
							this.allCheckBox = document.createElement("<input type='checkbox'/>");
							break;
						case "9":
						case "10":
						default:
							this.allCheckBox = document.createElement("input");
							this.allCheckBox.setAttribute("type", "checkbox");
					}
				} else {
					this.allCheckBox = document.createElement("input");
					this.allCheckBox.setAttribute("type", "checkbox");
				}
				
				colCheck.appendChild(this.allCheckBox);
				colCheck.setAttribute("class", "chkbox");
				
				this.allCheckBox.setAttribute("id", this.id + "_allCheck");
				
				var allCheckLabel = document.createElement("label");
				allCheckLabel.innerHTML = "全选";
				allCheckLabel.setAttribute("for", this.allCheckBox.getAttribute("id"));
				
				colCheck.appendChild(allCheckLabel);
				
				// event handler
				org.gridsofts.ext.Handler.add(this.allCheckBox, "click", function () {
					
					var bindChkId = this.getAttribute("bindChkId");
					var chkIdAry = bindChkId ? bindChkId.split(",") : bindChkId;
					
					for (var i = 0; chkIdAry && i < chkIdAry.length; i++) {
						
						var chk = document.getElementById(chkIdAry[i]);
						
						if (chk) {
							chk.checked = this.checked;
						}
					}
				});
			}
			
			for (var i = 0; i < this.headers.length; i++) {
				
				var th = document.createElement("th");
				tr.appendChild(th);

				th.innerHTML = this.headers[i]["label"];
				
				if (this.headers[i]["styleName"]) {
					th.setAttribute("class", this.headers[i]["styleName"]);
				}
			}
			
			// turn page
			var turnPagerDiv = document.createElement("div");
			canvas.appendChild(turnPagerDiv);
			
			turnPagerDiv.setAttribute("id", this.id + "_simpleTurnPager");
			turnPagerDiv.setAttribute("class", "SimpleTurnPager");
			
			// 加载数据
			if (this.autoLoad) {
				this.load();
			}
		},
		
		/**
		 * 删除表格除表头外的所有行
		 */
		removeAll:	function () {
			
			var table = document.getElementById(this.id);
			var rowsLen = table.rows.length;
			
			for (var i = 1; i < rowsLen; i++) {
				table.deleteRow(1);
			}
			
			// 清除绑定的复选框ID
			if (this.showCheck && this.allCheckBox) {
				this.allCheckBox.checked = false;
				this.allCheckBox.setAttribute("bindChkId", "");
			}
		},
		
		/**
		 * 缓存数据，递归
		 * 
		 * @param data
		 * @param row
		 * @param parentProName
		 */
		cacheData: function (data, row, parentName) {
			
			if (!parentName) {
				parentName = "";
			} else {
				parentName += ".";
			}
			
			for (var p in data) {
				
				if (data[p]) {
					
					if (typeof data[p] == "object") {
						
						if (data[p]["#cdata-section"]) {
							row.setAttribute(parentName + p, data[p]["#cdata-section"]);
						}
						
						this.cacheData(data[p], row, parentName + p);
					} else {
						row.setAttribute(parentName + p, data[p]);
					}
				}
			}
		},
		
		/**
		 * 插入行
		 */
		appendDataRow:	function (data, dataIndex) {
			
			if (data && typeof data == "object") {
			
				var table = document.getElementById(this.id);
				var row = table.insertRow(table.rows.length);
				
				// id
				row.setAttribute("_rowId", this.id + "_row_" + dataIndex);
				
				// cache data
				this.cacheData(data, row);
				
				// event handler
				if (this.onRowClick && typeof this.onRowClick == "function") {
					org.gridsofts.ext.Handler.add(row, "click", this.onRowClick);
				}
				if (this.onRowDblClick && typeof this.onRowDblClick == "function") {
					org.gridsofts.ext.Handler.add(row, "dblclick", this.onRowDblClick);
				}
				if (this.onRowMouseOver && typeof this.onRowMouseOver == "function") {
					org.gridsofts.ext.Handler.add(row, "mouseover", this.onRowMouseOver);
				}
				if (this.onRowMouseOut && typeof this.onRowMouseOut == "function") {
					org.gridsofts.ext.Handler.add(row, "mouseout", this.onRowMouseOut);
				}
				
				// columns
				var colIndex = 0;
				
				if (this.showCheck) {
					
					var chkId = this.id + "_chk_" + dataIndex;
					var html = "<input type=\"checkbox\" id=\"" + chkId 
								+ "\" value=\"" + dataIndex + "\"/>";
					
					var col = row.insertCell(colIndex++);
					col.innerHTML = html;
					col.setAttribute("class", "chkbox");
					
					var bindChkId = this.allCheckBox.getAttribute("bindChkId");
					this.allCheckBox.setAttribute("bindChkId", (bindChkId ? bindChkId : "") + chkId + ",");
					
					// checkbox event handler
					var chk = document.getElementById(chkId);
					org.gridsofts.ext.Handler.add(chk, "click", function (event) {
						if (event.stopPropagation) {
							event.stopPropagation();
						}
						else if (window.event) {
							window.event.cancelBubble = true;
						} 
					});
				}
	
				for (var i = 0; i < this.headers.length; i++, colIndex++) {
					
					// 查找自定义渲染器
					if (this.renderer[this.headers[i]["field"]]) {
						
						this.renderer[this.headers[i]["field"]](dataIndex, colIndex, row);
						
					}
					// 默认渲染
					else {
						
						// 创建单元格
						var col = row.insertCell(colIndex);
						
						// 查找列值
						var colVal = null;
						if (/^\[rownum\]$/i.test(this.headers[i]["field"])) {
							colVal = dataIndex + 1;
						} else {
							colVal = row.getAttribute(this.headers[i]["field"]);
						}
						
						col.innerHTML = colVal ? colVal : "";
						
						if (this.headers[i]["styleName"]) {
							col.setAttribute("class", this.headers[i]["styleName"]);
						}
					}
				}
			}
		},
		
		/**
		 * 加载数据
		 */
		load:	function (data) {
			
			if (this.onBeforeLoad && typeof this.onBeforeLoad == "function") {
				this.onBeforeLoad(this, data);
			}
			
			org.gridsofts.ajax.send(this, {
						url: this.dataSource,
						dataType: this.dataType,
						method: this.method,
						data: data,
						onComplete: this.onCompleteFunc,
						onError: this.onErrorFunc
					});
		},
		
		/**
		 * 刷新分页控制栏
		 */
		flushTurnPager:	function (response) {
			
			var turnPagerDiv = document.getElementById(this.id + "_simpleTurnPager");
			
			// clean
			turnPagerDiv.innerHTML = "";
			
			if (this.pagingAction == undefined) {
				return;
			}
			
			if (!response 
				|| !response["pageCount"] 
				|| !response["pageNumber"] 
				|| !response["rowCount"]) {
				
				return;
			}
			
			// 在当前页面两侧填充的页面数
			var fill = parseInt(response["pageCount"]) >= 100 ? 10 : 5;

			var start = parseInt(response["pageNumber"]) - fill;
			var end = parseInt(response["pageNumber"]) + fill;

			if (start < 1) {
				start = 1;
			}
			if (end > parseInt(response["pageCount"])) {
				end = parseInt(response["pageCount"]);
			}

			// row count
			var rowCountSpan = document.createElement("span");
			turnPagerDiv.appendChild(rowCountSpan);
			
			rowCountSpan.innerHTML = "总计 " + response["rowCount"] + " 行记录";
			rowCountSpan.setAttribute("id", this.id + "_simpleTurnPager_rowCount");
			rowCountSpan.setAttribute("class", "SimpleTurnPager_rowCount");
			
			// previous
			if (parseInt(response["pageNumber"]) > 1) {
				
				var prevSpan = document.createElement("span");
				turnPagerDiv.appendChild(prevSpan);
				
				prevSpan.setAttribute("id", this.id + "_simpleTurnPager_prev");
				prevSpan.setAttribute("class", "SimpleTurnPager_prev");
				
				var prevA = document.createElement("a");
				prevSpan.appendChild(prevA);
				
				prevA.innerHTML = "上一页";
				prevA.setAttribute("href", this.pagingAction.replace(/#\[TP\]/gi, parseInt(response["pageNumber"]) - 1));
			}

			// first
			if (start > 1) {
				
				var firstSpan = document.createElement("span");
				turnPagerDiv.appendChild(firstSpan);
				
				firstSpan.setAttribute("id", this.id + "_simpleTurnPager_first");
				firstSpan.setAttribute("class", "SimpleTurnPager_first");
				
				var firstA = document.createElement("a");
				firstSpan.appendChild(firstA);
				
				firstA.innerHTML = "1 ...";
				firstA.setAttribute("href", this.pagingAction.replace(/#\[TP\]/gi, 1));
			}

			// left side
			for (var i = start; i < parseInt(response["pageNumber"]); i++) {
				
				var lefSideSpan = document.createElement("span");
				turnPagerDiv.appendChild(lefSideSpan);
				
				lefSideSpan.setAttribute("id", this.id + "_simpleTurnPager_" + i);
				
				var leftSideA = document.createElement("a");
				lefSideSpan.appendChild(leftSideA);
				
				leftSideA.innerHTML = i;
				leftSideA.setAttribute("href", this.pagingAction.replace(/#\[TP\]/gi, i));
			}

			// current
			var curSpan = document.createElement("span");
			turnPagerDiv.appendChild(curSpan);

			curSpan.innerHTML = response["pageNumber"];
			curSpan.setAttribute("id", this.id + "_simpleTurnPager_f");
			curSpan.setAttribute("class", "current");

			// right side
			for (var i = parseInt(response["pageNumber"]) + 1; i <= end; i++) {
				
				var rightSideSpan = document.createElement("span");
				turnPagerDiv.appendChild(rightSideSpan);
				
				rightSideSpan.setAttribute("id", this.id + "_simpleTurnPager_" + i);
				
				var rightSideA = document.createElement("a");
				rightSideSpan.appendChild(rightSideA);
				
				rightSideA.innerHTML = i;
				rightSideA.setAttribute("href", this.pagingAction.replace(/#\[TP\]/gi, i));
			}

			// last
			if (end < parseInt(response["pageCount"])) {
				
				var lastSpan = document.createElement("span");
				turnPagerDiv.appendChild(lastSpan);
				
				lastSpan.setAttribute("id", this.id + "_simpleTurnPager_last");
				lastSpan.setAttribute("class", "SimpleTurnPager_last");
				
				var lastA = document.createElement("a");
				lastSpan.appendChild(lastA);
				
				lastA.innerHTML = "... " + response["pageCount"];
				lastA.setAttribute("href", this.pagingAction.replace(/#\[TP\]/gi, response["pageCount"]));
			}

			// next
			if (parseInt(response["pageNumber"]) < parseInt(response["pageCount"])) {
				
				var nextSpan = document.createElement("span");
				turnPagerDiv.appendChild(nextSpan);
				
				nextSpan.setAttribute("id", this.id + "_simpleTurnPager_next");
				nextSpan.setAttribute("class", "SimpleTurnPager_next");
				
				var nextA = document.createElement("a");
				nextSpan.appendChild(nextA);
				
				nextA.innerHTML = "下一页";
				nextA.setAttribute("href", this.pagingAction.replace(/#\[TP\]/gi, parseInt(response["pageNumber"]) + 1));
			}
		},
		
		/**
		 * 数据加载完毕
		 */
		onCompleteFunc:	function ($this, response) {
			
			if ($this.onAfterLoad && typeof $this.onAfterLoad == "function") {
				$this.onAfterLoad($this, response);
			}
			
			$this.removeAll();
			
			if (response && typeof response == "object" 
				&& $.isArray(response["data"])) {
				
				for (var i = 0; i < response["data"].length; i++) {
					$this.appendDataRow(response["data"][i], i);
				}
			}
			
			// flush turnpager
			$this.flushTurnPager(response);
		},
		
		/**
		 * 数据加载出错
		 */
		onErrorFunc:	function ($this, data) {
	
			if ($this.onAfterLoad && typeof $this.onAfterLoad == "function") {
				$this.onAfterLoad($this, data);
			}
			
			$this.removeAll();
		}
	}
});


 
/**
 * 结果集类
 * 		用来表示行列式结果集，支持分页操作。
 * 
 * 作者：赵磊 
 */
org.gridsofts.ajax.ResultSet = org.gridsofts.util.Class.define({
	name:	"ResultSet",
	
	construct:
		function () {
			this._rows = new Array();
			
			this._rowPoint = -1;		// 当前行指针
			this._pagePoint = 1;		// 当前页指针（从1开始）
			
			this._maxPageRows = 10;		// 每页显示的最大记录行数（默认10）
			this._maxPageCount = 0;		// 总页数
			this._maxRowCount = 0;		// 总记录数
			
			// 用户可以选择在构造对象时，设定每页显示的最大记录行数
			if (arguments.length > 0 && typeof arguments[0] == "number")
				this._maxPageRows = arguments[0] > 0 ? arguments[0] : 1;
		},
		
	methods:	{
		
		/**
		 * 获取指定行指定列的值。
		 * 注：
		 * 	1、如果传入一个参数，则认为是列名；
		 * 	2、如果传入两个以上的参数，则认为依次是行索引、列名、...；
		 * 	3、行索引从0开始。
		 */
		get:	function () {
			
			if (arguments.length == 1) {
				
				var row = this._rows[this._rowPoint];
				
				if (row && typeof row == "object") {
					return row[arguments[0]];
				}
				
			} else if (arguments.length > 1) {
				
				var row = this._rows[arguments[0]];
				
				if (row && typeof row == "object") {
					return row[arguments[1]];
				}
				
			}
			
			return null;
		},
		
		/* 行指针下移，如果已到末尾则返回false，否则返回true。 */
		next:	function () {
			
			this._rowPoint++;
			
			if (this._rowPoint < this._maxRowCount
				&& this._rowPoint < this._maxPageRows * this._pagePoint) {
				
				return true;
			}
			
			return false;
		},
		
		/* 跳至指定页 */
		gotoPage:	function (to) {
			this._pagePoint = to < this._maxPageCount ? to > 1 ? to : 1 : this._maxPageCount;
			this._rowPoint = this._maxPageRows * (this._pagePoint - 1) - 1;
		},
		
		/* 追加结果集记录 */
		append:	function (data) {
			this._rows = this._rows.concat(data);
			
			// 计算最大页数等
			this._maxRowCount = this._rows.length;
			this._maxPageCount = this._maxRowCount % this._maxPageRows > 0 
								? Math.floor(this._maxRowCount / this._maxPageRows) + 1 
								: Math.floor(this._maxRowCount / this._maxPageRows);
			
			this._pagePoint = this._pagePoint > this._maxPageCount ? this._maxPageCount : this._pagePoint;
			this._rowPoint = this._maxPageRows * (this._pagePoint - 1) - 1;
		},
		
		/* 从当前结果集删除一行 */
		remove:	function (index) {
			this._rows.splice(index, 1);
			
			// 计算最大页数等
			this._maxRowCount = this._rows.length;
			this._maxPageCount = this._maxRowCount % this._maxPageRows > 0 
								? Math.floor(this._maxRowCount / this._maxPageRows) + 1 
								: Math.floor(this._maxRowCount / this._maxPageRows);
			
			this._pagePoint = this._pagePoint > this._maxPageCount ? this._maxPageCount : this._pagePoint;
			this._rowPoint = this._maxPageRows * (this._pagePoint - 1) - 1;
		},
		
		/* 更新结果集中的指定行 */
		update:	function (index, data) {
			this._rows.splice(index, 1, data);
		},
		
		/* 重置行指针 */
		beforeFirst:	function () {
			this._rowPoint = this._maxPageRows * (this._pagePoint - 1) - 1;
		},
		
		/* 获取总记录行数 */
		size:	function () {
			return this._maxRowCount;
		},
		
		/* 获取总页数 */
		getPageCount:	function () {
			return this._maxPageCount;
		},
		
		/* 获取每页可显示的最大记录数 */
		getPageRows:	function () {
			return this._maxPageRows;
		},
		
		/* 获取当前页码 */
		getPageNum:	function () {
			return this._pagePoint;
		},
		
		/* 获取当前行序号 */
		getRowNum:	function () {
			return this._rowPoint + 1;
		}
	}
});